package main

import (
	"fmt"
)

func main() {
	// 数组中存在的问题是大小固定，而且不能修改
	//var courses []string
	courses := []string{"python", "java"}
	fmt.Println(len(courses))
	fmt.Printf("%T\n", courses)

	//切片另外一种初始化方法make
	courses1 := make([]string, 5)
	fmt.Println(courses1, len(courses1))
	fmt.Printf("%T\n", courses1)
	// 第三种方法:通过数组变成一个切片
	var courses2 = [5]string{"django", "flask", "scrapy"}
	subCourses := courses2[:]
	subCourses[0] = "hcj" //对于数组的切片进行修改,会影响到数组上的值
	fmt.Println(subCourses)
	fmt.Printf("%T\n", subCourses)

	//第四种方式 new
	subCourses1 := *new([]int)
	fmt.Println(subCourses1)
	//数组的传递是值传递,但是切片的传递是引用传递
	fmt.Println(subCourses)
	subCourses2 := subCourses[1:2]
	fmt.Println(subCourses2)

	//append 可以向切片追加元素,追加的时候底层切片的值也会对应改变
	fmt.Printf("%T,%v\n", subCourses2, subCourses2)
	subCourses2 = append(subCourses2, "java", "java1")
	fmt.Println(subCourses, subCourses2)
	fmt.Printf("%T,%v\n", subCourses2, subCourses2)

	//切片可以进行拷贝
	subCourses3 := make([]string, len(subCourses))
	fmt.Println(len(subCourses3))
	copy(subCourses3, subCourses2)
	fmt.Println(subCourses3)

	//切片append 切片
	appendCourse := []string{"java", "java1"}
	subCourses2 = append(subCourses2, appendCourse...)
	fmt.Println(subCourses2)

	//切片中删除元素
	var deleteCourse = [5]string{"django", "flask", "scrapy", "java", "python"}
	courseSlice := deleteCourse[:]
	courseSlice = append(courseSlice[:1], courseSlice[2:]...)
	fmt.Println(courseSlice)

	//如何判断元素是否在切片中
	for i := 0; i < len(courseSlice); i++ {
		if courseSlice[i] == "django" {
			fmt.Println("django在其中")
		}
	}
	//slice的原理，因为很多底层的知识相对来说很多时候并不难而是需要花费大量时间去理解
	//1.第一个现象
	a := make([]int, 0)
	fmt.Printf("类型是%T", a)
	b := []int{1, 2, 3}
	fmt.Println(copy(a, b))
	fmt.Println(a)
	//第二个现象
	c := b[:]
	c[0] = 8
	fmt.Println(c, b)
	//3.第三个现象
	c = append(c, 9)
	fmt.Println(c, b)
	c[0] = 9
	fmt.Println(c, b)
	//4.第四个现象
	//cap指的是容量
	fmt.Println(len(c), cap(c))

	//假设有一个值 实际上申请数组的时候可能是两个，如果后续要增加数据那么就直接添加数据到结尾，这个时候我不要额外重新申请
	//切片有不同的初始化方式
	//1.使用make方法初始化
	d := make([]int, 5)
	fmt.Printf("len=%d  cap=%d\n", len(d), cap(d))
	//2.通过数组取得切片
	data := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := data[2:4]
	fmt.Printf("len=%d,cap=%d\n", len(slice), cap(slice))
	slice = append(slice, 111)
	fmt.Println(data)
	//3.
	slice2 := []int{1, 2, 3}
	fmt.Printf("len=%d,cap=%d\n", len(slice2), cap(slice2))

	//切片扩容问题， 扩容阶段会影响速度， python的list中底层实际上也是数组，也会面临动态扩容的问题，python的list中数据类型可以不一致
	oldSlice := make([]int, 0)
	fmt.Printf("len=%d, cap=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 1)
	fmt.Printf("len=%d, cap=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 2)
	fmt.Printf("len=%d, cap=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 3)
	fmt.Printf("len=%d, cap=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 4)
	oldSlice = append(oldSlice, 5)
	oldSlice = append(oldSlice, 4)
	oldSlice = append(oldSlice, 5)
	oldSlice = append(oldSlice, 4)
	oldSlice = append(oldSlice, 5)
	fmt.Printf("len=%d, cap=%d\n", len(oldSlice), cap(oldSlice))
	/*
		Go 中切片扩容的策略是这样的：

		首先判断，如果新申请容量（cap）大于2倍的旧容量（old.cap），最终容量（newcap）就是新申请的容量（cap）
		否则判断，如果旧切片的长度小于1024，则最终容量(newcap)就是旧容量(old.cap)的两倍，即（newcap=doublecap）
		否则判断，如果旧切片长度大于等于1024，则最终容量（newcap）从旧容量（old.cap）开始循环增加原来的 1/4，即（newcap=old.cap,for {newcap += newcap/4}）直到最终容量（newcap）大于等于新申请的容量(cap)，即（newcap >= cap）
		如果最终容量（cap）计算值溢出，则最终容量（cap）就是新申请容量（cap）
	*/
	//如果小于1024 扩容的速度是2倍 如果大于了1024 扩容的速度就是1.25

	//切片来说 1. 底层是数组，如果是基于数组产生的 会有一个问题就是会影响原来的数组。
	//2. 切片的扩容机制
	//3. 切片的传递是引用传递
	oldArr := [3]int{1, 2, 3}
	newArr := oldArr
	newArr[0] = 5
	fmt.Println(newArr, oldArr)

	oldSlice = []int{1, 2, 3}
	newSlice := oldSlice
	newSlice[0] = 5
	fmt.Println(oldSlice)
	//go语言中slice的原理讲解很重要，这个有坑（对于初学者），有经验的程序员觉得这个不是坑
	//程序员也是消费者-java c++ go -静态语言是站在对处理器的角度考虑， python - 才会站在使用者的角度上去考虑，对于处理器就不友好
	//当make遇到了append容易出现的坑
	s1 := make([]int, 0)
	s1 = append(s1, 6)
	//对于很多初学者来说我们期望的是只有一个数字就是6
	fmt.Println(s1)
	//很多人对make函数产生了一个假象 ，s1 := make([]int, 5) 好比是python的 s1 = []
}
